#include "BlazeStdAfx.h"

#include "BlazeBuilder.h"


CLight *CBuilder::CreateLight(unsigned int iNodeID)
{
	CIntermediateLight *pILight = NULL;

	LOGDEBUG "Verify Node ID");

	// Verify Node ID
	INode* pRoot = GetNodeFromID(iNodeID);
	if(!pRoot) return NULL;

	LOGDEBUG "Get object on node");

	// Retrieve object on node
	ObjectState os = pRoot->EvalWorldState(0, TRUE);
	Object* pObj = os.obj;
	if(!pObj) return NULL;

	LOGDEBUG "Validate");

	// We only use the Light objects
	if(pObj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) return NULL;
	if(pObj->SuperClassID() != LIGHT_CLASS_ID) return NULL;

	LightState ls;
	((LightObject*)pObj)->EvalLightState(0, Interval(0,0), &ls);
	if (ls.type == AMBIENT_LGT)
		return NULL;

	pILight = new CIntermediateLight(iNodeID);
	SetInstanceIDs(pILight);

	switch(ls.type)
	{
	case OMNI_LGT:
		pILight->SetValue("type", "point");
		break;

	case SPOT_LGT:
		pILight->SetValue("type", "spot");
		break;

	case DIRECT_LGT:
		pILight->SetValue("type", "directional");
		break;
	}

	pILight->SetValue("visible", ls.on != 0);

	pILight->SetValue("castShadows", ls.shadow != 0);

	Ogre::ColourValue col = Ogre::ColourValue(ls.color.r, ls.color.g, ls.color.b) * ls.intens;
	pILight->SetValue("colourDiffuse", ls.affectDiffuse ? col : Ogre::ColourValue::Black);
	pILight->SetValue("colourSpecular", ls.affectSpecular ? col : Ogre::ColourValue::Black);

	if (ls.type == SPOT_LGT)
	{
		pILight->SetValue("lightRange.inner", Ogre::Math::DegreesToRadians(ls.hotsize));
		pILight->SetValue("lightRange.outer", Ogre::Math::DegreesToRadians(ls.fallsize));
		pILight->SetValue("lightRange.falloff", 1.0f);
	}

	if (ls.type == SPOT_LGT || ls.type == DIRECT_LGT)
	{ // Max lights look along -z, which is -y in Ogre
		pILight->SetValue("normal", -Ogre::Vector3::UNIT_Y);
	}

	INode* pTarget = pRoot->GetTarget();
	if(pTarget)
	{
		pILight->SetValue("trackTarget.nodeName", pTarget->GetName());
	}

	// TODO: lightAttenuation: range, constant, linear, quadratic

	return pILight;
}

CCamera *CBuilder::CreateCamera(unsigned int iNodeID)
{
	CIntermediateCamera* pICamera = NULL;

	LOGDEBUG "Verify Node ID");

	// Verify Node ID
	INode* pRoot = GetNodeFromID(iNodeID);
	if(!pRoot) return NULL;

	LOGDEBUG "Get object on node");

	// Retrieve object on node
	ObjectState os = pRoot->EvalWorldState(0, TRUE);
	Object* pObj = os.obj;
	if(!pObj) return NULL;

	LOGDEBUG "Validate");

	// We only use the Camera objects
	if(pObj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) return NULL;
	if(pObj->SuperClassID() != CAMERA_CLASS_ID) return NULL;

	CameraState cs;
	((CameraObject*)pObj)->EvalCameraState(0, Interval(0,0), &cs);

	pICamera = new CIntermediateCamera(iNodeID);
	SetInstanceIDs(pICamera);

	pICamera->SetValue("fov", cs.fov * 57.295779513f);

	pICamera->SetValue("projectionType", cs.isOrtho ? "orthographic" : "perspective");

	pICamera->SetValue("clipping.near", cs.hither);
	pICamera->SetValue("clipping.far", cs.yon);

	INode* pTarget = pRoot->GetTarget();
	if(pTarget)
	{
		pICamera->SetValue("trackTarget.nodeName", pTarget->GetName());
	}

	// to compensate for Ogre cameras looking along a different axis than Max cameras
	pICamera->SetValue("rotation", Ogre::Quaternion(-Ogre::Radian(Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X));

	return pICamera;
}

CIntermediateMesh* CBuilder::CreateMesh(unsigned int iNodeID)
{
	LOGDEBUG "Verify Node ID");

	// Verify Node ID
	INode* pRoot = GetNodeFromID(iNodeID);
	if(!pRoot) return NULL;

	LOGDEBUG "Get object on node");

	// Retrieve object on node
	ObjectState os = pRoot->EvalWorldState(0, TRUE);
	Object* pObj = os.obj;
	if(!pObj) return NULL;

	LOGDEBUG "Validate");

	// We only use the Mesh objects
	if(pObj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) return NULL;
	if(pObj->SuperClassID() != GEOMOBJECT_CLASS_ID) return NULL;
	if(!pObj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) return NULL;

	LOGDEBUG "->TRIOBJ_CLASS");

	// Convert object into triangle object (mesh data)
	TriObject* pTri = (TriObject*)pObj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0));
	if(!pTri) return NULL;

	bool bDeleteTri = (pObj != pTri) ? true : false;

	LOGDEBUG "Get mesh");
	Mesh* pMesh = &pTri->GetMesh();
	if(!pMesh || !pMesh->getNumFaces())
	{
		if(bDeleteTri) delete pTri;
		return NULL;
	}

	// CALL ON_CREATE_INTERMEDIATE_MESH_START()

	LOGDEBUG "Get material");
	Mtl* maxMtl = pRoot->GetMtl();
	if(maxMtl == NULL)
	{
		StdMat* pBlankMat = NewDefaultStdMat();

		pBlankMat->SetDiffuse( Color(1,1,1), 0);
		Ogre::String name = Ogre::String(pRoot->GetName()) + Ogre::StringConverter::toString(pRoot->GetHandle()) + Ogre::String("_material") ;
		pBlankMat->SetName( name.c_str() );
		maxMtl = (Mtl*)pBlankMat;
	}
	BOOL bMultiMat = maxMtl->IsMultiMtl();

	LOGDEBUG "Construct intermediate mesh");
	unsigned int iNumTriangles = pMesh->getNumFaces();
	CIntermediateMesh* pIMesh = new CIntermediateMesh(iNumTriangles, iNodeID);
	SetInstanceIDs(pIMesh);

	pIMesh->SetValue("castShadows", pRoot->CastShadows() ? "true" : "false");

	if(m_bExportSkeleton)
	{
		LOGDEBUG "Construct intermediate skeletonbuilder");
		if(m_pSkeletonBuilder == NULL)
			m_pSkeletonBuilder = new CIntermediateBuilderSkeleton();

		LOGDEBUG "Build skeleton");
		m_pSkeletonBuilder->BuildIntermediateSkeleton(pRoot);

		LOGDEBUG "Set skeleton");
		pIMesh->SetSkeleton( m_pSkeletonBuilder->GetSkeleton() );
	}

	LOGDEBUG "Iterate triangles");
	//
	const Matrix3& tm = pRoot->GetObjTMAfterWSM(0);
	unsigned int wind[3] = { 0, 1, 2 };
	if(TMNegParity(tm)) { wind[0] = 2; wind[2] = 0; }

	unsigned int iBoneIndex = 0;
	unsigned int iVIndex = 0;
	for(unsigned int x = 0; x < iNumTriangles; x++)
	{
		// Order the face. Due to flat shading where the 
		// first vertex in a face is used to color the entire face. 
		// So faces in the same polygon should share the same start vertex.
		//unsigned int wind[3] = { 0, 1, 2 };
		//if((x%2))
		//{
		//	wind[0]=1;
		//	wind[1]=2; // should be 2,0,1 if we use OpenGL
		//	wind[2]=0;
		//}

		// Create a flat indexing to the verticies.

		CTriangle& tri = pIMesh->GetTriangle(x);
		tri.m_Vertices[wind[0]] = iVIndex++;
		tri.m_Vertices[wind[1]] = iVIndex++;
		tri.m_Vertices[wind[2]] = iVIndex++;

		Mtl* pMat = maxMtl;
		if (bMultiMat)
		{
			int subCount = maxMtl->NumSubMtls();
			MtlID matID = pMesh->getFaceMtlIndex(x) %subCount;
			pMat = maxMtl->GetSubMtl( matID );
			if(pMat == NULL)
			{
				char buf[256];
				sprintf (buf, "Mesh \"%s\" references submaterial ID %d.  No such submaterial in Material \"%s\".",
					pRoot->GetName(), matID, maxMtl->GetName());
				return NULL;
			}
		}

		tri.m_pMaterial = CreateMaterial( pMat );

		// CALL ON_CREATE_INTERMEDIATE_MESH_TRIANGLE_LOOP(pMesh, idx, tri.m_Vertices[wind[j]], x)

		//if(m_pSkeletonBuilder != NULL)
		if(m_bExportSkeleton)
		{
			for (int j=0; j<3; j++)
			{
				// get current index from max mesh
				int idx = pMesh->faces[x].v[j]; 
				m_pSkeletonBuilder->SetVertexData( idx, tri.m_Vertices[wind[j]] );
			}
		}
	}

	if(m_bExportSkeleton)
	{
		LOGDEBUG "Finalizing intermediate skeleton");
		CIntermediateBuilder::Get()->GetSkeletonBuilder()->Finalize();
	}

	LOGDEBUG "CIntermediate::CreateMesh() - Building Material List..");

	pIMesh->BuildMaterialList();
	//	pIMesh->BuildSubmeshIndexMaps();


	// CALL ON_CREATE_INTERMEDIATE_MESH_END()
	LOGDEBUG "Delete pTri");

	if(bDeleteTri) delete pTri;

	return pIMesh;
}
